package com.cwits.wifi.manager;

import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;

import com.cwits.wifi.MainApplication;
import com.cwits.wifi.util.ClientManager;
import com.cwits.wifi.util.Dbug;
import com.cwits.wifi.util.IConstant;
import com.jieli.lib.dv.control.DeviceClient;
import com.jieli.lib.dv.control.connect.listener.OnConnectStateListener;
import com.jieli.lib.dv.control.connect.response.SendResponse;
import com.jieli.lib.dv.control.json.bean.CmdInfo;
import com.jieli.lib.dv.control.json.bean.NotifyInfo;
import com.jieli.lib.dv.control.receiver.listener.OnNotifyListener;
import com.jieli.lib.dv.control.utils.Constants;
import com.jieli.lib.stream.beans.StateInfo;
import com.jieli.lib.stream.tools.CommandHub;
import com.jieli.lib.stream.util.ICommon;

import java.util.HashSet;
import java.util.Set;

/**
 * @author zqjasonZhong
 *         date : 2017/7/13
 */
public class ControlManager implements MConstant, CommandHub.OnDeviceListener, OnConnectStateListener, OnNotifyListener{

    private static final String TAG = "ControlManager";
    private static ControlManager manager;
    private CommandHub mCommandHub;
    private DeviceClient deviceClient;
    private MainApplication mApplication;

    private Set<SocketConnectListener> connectListenerSet;
    private Set<SocketReceiveCallback> receiveCallbackSet;

    private int socketType;
    private int socketStatus;
    private String connectIP;

    private boolean is_connect_dv12 = false;
    private boolean is_connect_dv16 = false;

    private Handler mHandler = new Handler(Looper.myLooper());

    public ControlManager(){
        mApplication = MainApplication.getApplication();
        mCommandHub = CommandHub.getInstance();
        deviceClient = ClientManager.getClient();
        socketType = SOCKET_NONE;

        connectListenerSet = new HashSet<>();
        receiveCallbackSet = new HashSet<>();
    }

    public static ControlManager getInstance() {
        if(manager == null){
            synchronized (ControlManager.class){
                if(manager == null){
                    manager = new ControlManager();
                }
            }
        }
        return manager;
    }


    /* DV12 Connect Listener*/
    @Override
    public void onConnected() {
        socketType = SOCKET_DV12;
        notifyConnectChange(SOCKET_STATUS_CONNECTED);
    }

    /* DV12 error listener*/
    @Override
    public void onError(int errorType) {
        if(socketType == SOCKET_DV12){
            socketType = 0;
            connectIP = null;
            switch (errorType){
                case CommandHub.ERROR_CONNECTION_TIMEOUT:
                    notifyConnectChange(SOCKET_STATUS_CONNECTION_TIMEOUT);
                    break;
                case CommandHub.ERROR_CONNECTION_EXCEPTION:
                    notifyConnectChange(SOCKET_STATUS_EXCEPTION);
                    break;
            }
        }else{
            if(errorType == CommandHub.ERROR_CONNECTION_TIMEOUT){
                if(is_connect_dv16){
                    notifyConnectChange(SOCKET_STATUS_NO_168DVRCAR);
                }else{
                    is_connect_dv12 = true;
                }
            }

        }
    }

    /*DV12 data receive callback*/
    @Override
    public void onReceive(StateInfo stateInfo) {
        notifyReceiveCallbackFromDV12(stateInfo);
    }

    /* DV16 Connect and Error Listener*/
    @Override
    public void onStateChanged(Integer state) {
        if(state == Constants.DEVICE_STATE_CONNECTED){
            socketType = SOCKET_DV16;
            notifyConnectChange(SOCKET_STATUS_CONNECTED);
        }else{
            if(socketType == SOCKET_DV16){
                socketType = 0;
                connectIP = null;
                switch (state){
                    case Constants.DEVICE_STATE_UNREADY:
                    case Constants.DEVICE_STATE_CONNECTING:
                        notifyConnectChange(SOCKET_STATUS_CONNECTING);
                        break;
                    case Constants.DEVICE_STATE_DISCONNECTED:
                        notifyConnectChange(SOCKET_STATUS_DISCONNECTED);
                        break;
                    case Constants.DEVICE_STATE_CONNECTION_TIMEOUT:
                        notifyConnectChange(SOCKET_STATUS_CONNECTION_TIMEOUT);
                        break;
                    case Constants.DEVICE_STATE_EXCEPTION:
                        notifyConnectChange(SOCKET_STATUS_EXCEPTION);
                        break;
                }
            }else{
                if(state == Constants.DEVICE_STATE_CONNECTION_TIMEOUT){
                    if(is_connect_dv12){
                        notifyConnectChange(SOCKET_STATUS_NO_168DVRCAR);
                    }else{
                        is_connect_dv16 = true;
                    }
                }
            }
        }
    }

    /* DV16 data receive callback*/
    @Override
    public void onNotify(NotifyInfo data) {
        notifyReceiveCallbackFromDV16(data);
    }

    /**
     * 注册Socket连接状态监听器
     * @param listener  连接状态监听器
     */
    public void registerSocketConnectListener(SocketConnectListener listener){
        if(listener != null){
            if (connectListenerSet.add(listener)) {
                Dbug.i(TAG, "add SocketConnectListener success, listener : " + listener);
            } else {
                Dbug.w(TAG, "SocketConnectListener is existed.");
            }
        }
    }

    /**
     * 注销Socket连接状态监听器
     * @param listener  连接状态监听器
     */
    public void unregisterSocketConnectListener(SocketConnectListener listener){
        if(listener != null && connectListenerSet.size() > 0){
            if(connectListenerSet.remove(listener)){
                Dbug.i(TAG, "remove SocketConnectListener success, listener : "+ listener);
            }else{
                Dbug.w(TAG, "SocketConnectListener does not exist.");
            }
        }
    }

    /**
     * 注册Socket数据回调
     * @param callback  数据回调
     */
    public void registerSocketReceiveCallback(SocketReceiveCallback callback){
        if(callback != null){
            if(receiveCallbackSet.add(callback)){
                Dbug.i(TAG, "add SocketReceiveCallback success, callback : "+ callback);
            }else{
                Dbug.w(TAG, "SocketReceiveCallback is existed.");
            }
        }
    }

    /**
     * 注销Socket数据回调
     * @param callback  数据回调
     */
    public void unregisterSocketReceiveCallback(SocketReceiveCallback callback){
        if(callback != null && receiveCallbackSet.size() > 0){
            if(receiveCallbackSet.remove(callback)){
                Dbug.i(TAG, "remove SocketReceiveCallback success, callback : "+ callback);
            }else{
                Dbug.w(TAG, "SocketReceiveCallback does not exist.");
            }
        }
    }

    /**
     * 通知Socket状态改变
     * @param status  状态
     */
    private void notifyConnectChange(Integer status){
        socketStatus = status;
        if(connectListenerSet.size() > 0){
            for (SocketConnectListener listener : connectListenerSet){
                if(mHandler != null){
                    mHandler.post(new NotifySocketStatusRunnable(listener, status));
                }
            }
        }
    }

    private class NotifySocketStatusRunnable implements Runnable {
        private SocketConnectListener listener;
        private Integer status;

        NotifySocketStatusRunnable(SocketConnectListener listener, Integer status){
            this.listener = listener;
            this.status = status;
        }

        @Override
        public void run() {
            if(listener != null){
                listener.onSocketStatusChanged(status);
            }
        }
    }

    private void notifyReceiveCallbackFromDV12(StateInfo obj){
        if(obj != null && receiveCallbackSet.size() > 0){
            for (SocketReceiveCallback callback : receiveCallbackSet){
                if(mHandler != null){
                    mHandler.post(new NotifyReceiveCallbackRunnable(callback, obj, null));
                }
            }
        }
    }

    private void notifyReceiveCallbackFromDV16(NotifyInfo obj){
        if(obj != null && receiveCallbackSet.size() > 0){
            for (SocketReceiveCallback callback : receiveCallbackSet){
                if(mHandler != null){
                    mHandler.post(new NotifyReceiveCallbackRunnable(callback, null, obj));
                }
            }
        }
    }

    private class NotifyReceiveCallbackRunnable implements Runnable {
        private SocketReceiveCallback callback;
        private StateInfo stateInfo;
        private NotifyInfo notifyInfo;

        NotifyReceiveCallbackRunnable(SocketReceiveCallback callback, StateInfo stateInfo, NotifyInfo notifyInfo){
            this.callback = callback;
            this.stateInfo = stateInfo;
            this.notifyInfo = notifyInfo;
        }

        @Override
        public void run() {
            if(callback != null && stateInfo != null){
                callback.onReceive(stateInfo);
            }
            if(callback != null && notifyInfo != null){
                callback.onNotify(notifyInfo);
            }
        }
    }

    /**
     * 连接设备Socket
     * @param ip     设备的ip
     */
    public void connectSocket(String ip){
        is_connect_dv12 = false;
        is_connect_dv16 = false;
        //DV12 connect socket
        mCommandHub.setLocalAppVersion(String.valueOf(mApplication.getAppVersion()));
        mCommandHub.createClient(ip, ICommon.AP_MODE_DEVICE_PORT);
        mCommandHub.setOnDeviceListener(this);

        //DV16 connect socket
        deviceClient.connect(ip, IConstant.AP_MODE_PORT);
        deviceClient.registerConnectStateListener(this);
        deviceClient.registerNotifyListener(this);

        connectIP = ip;
        notifyConnectChange(SOCKET_STATUS_CONNECTING);
    }

    /**
     * 断开与设备的连接
     */
    public void disconnectSocket(){
        if(socketType == SOCKET_DV12){
            mCommandHub.sendCommand(ICommon.CTP_ID_DEFAULT, ICommon.CMD_DISABLE_DEVICE_WIFI, ICommon.ARGS_DISABLE_DEVICE_WIFI);
            mCommandHub.closeClient();
            notifyConnectChange(SOCKET_STATUS_DISCONNECTED);
        }else if(socketType == SOCKET_DV16){
            deviceClient.disconnect();
        }
        mCommandHub.setOnDeviceListener(null);
        deviceClient.unregisterConnectStateListener(this);
        deviceClient.unregisterNotifyListener(this);
        is_connect_dv12 = false;
        is_connect_dv16 = false;

        connectIP = null;
    }

    /**
     * 获得连接Socket的类型
     * @return {@link #SOCKET_DV12} or {@link #SOCKET_DV16}
     */
    public int getSocketType(){
        return socketType;
    }

    /**
     *获得当前连接状态
     * @return {@link MConstant#SOCKET_STATUS_CONNECTING}
     * or {@link MConstant#SOCKET_STATUS_CONNECTED}
     * or {@link MConstant#SOCKET_STATUS_DISCONNECTED}
     * or {@link MConstant#SOCKET_STATUS_CONNECTION_TIMEOUT}
     * or {@link MConstant#SOCKET_STATUS_EXCEPTION}
     */
    public int getSocketStatus(){
        return socketStatus;
    }

    /**
     * 检测Socket是否活着
     */
    public boolean isSocketAlive(){
        boolean isAlive = false;
        switch (socketType){
            case SOCKET_DV12:
                isAlive = mCommandHub.isActive();
                break;
            case SOCKET_DV16:
                isAlive = deviceClient.isConnected();
                break;
        }
        if(!isAlive){
            connectIP = null;
        }
        return isAlive;
    }

    /**
     * 获得连接的IP地址
     * @return 若连接中返回IP，否则返回null
     */
    public String getConnectIP() {
        return connectIP;
    }

    /**
     * 发送命令（DV12）
     *  @param ctpId        the ID of CTP
     * @param commandNumber the command number
     * @param args          the list of arguments passed to the {@code sendCommand}.
     */
    public void sendCommand(String ctpId, String commandNumber, String...args){
        if(socketType == SOCKET_DV12) {
            if(!TextUtils.isEmpty(ctpId) && !TextUtils.isEmpty(commandNumber)) {
                mCommandHub.sendCommand(ctpId, commandNumber, args);
            }else{
                Dbug.i(TAG, "params is error.");
            }
        }else{
            Dbug.w(TAG, "socket type is not dv12. please check!");
        }
    }

    /**
     * 发送询问命令（DV12）
     * @param ctpId          the ID of CTP
     * @param commandNumber  the command number
     */
    public void requestStatus(String ctpId, String commandNumber){
        if(socketType == SOCKET_DV12) {
            if(!TextUtils.isEmpty(ctpId) && !TextUtils.isEmpty(commandNumber)) {
                mCommandHub.requestStatus(ctpId, commandNumber);
            }
        }else{
            Dbug.w(TAG, "socket type is not dv12. please check!");
        }
    }

    /**
     * 发送命令数据
     * @param cmdInfo    命令信息
     * @param response   发送结果回调
     */
    public void sendData(CmdInfo cmdInfo, SendResponse response){
        if(socketType == SOCKET_DV16){
            if(cmdInfo != null){
                deviceClient.send(cmdInfo, response);
            }else{
                Dbug.i(TAG, "param is error.");
            }
        }else{
            Dbug.w(TAG, "socket type is not dv16. please check!");
        }
    }

    /**
     * 释放资源
     */
    public void release(){
        disconnectSocket();

        if(socketType != 0){
            socketType = 0;
        }

        if(manager != null){
            manager = null;
        }

        connectListenerSet.clear();
        receiveCallbackSet.clear();

        ClientManager.getClient().release();
    }
}
